/*
 * DIOC1.c
 *
 *  Created on: 12/11/2012
 *      Author: Richard
 */

#include "DIOC1.h"
#include "../../sensor.h"
#include "../../Application/DEB/DEBC1.h"

typedef struct
{
   uint32                  u32Pin;
   DIO_tenLogicLevel       enLogicLevel;
   DIO_tenDataDirection    enDirection;
   bool                    boInterruptEnable;
   bool                    boResistorEnable;
   DIO_tenPortSelect       enPeripheralMode;
   DIO_tenDriveStrength    enDriveStrength;
   void                    (*pfInterruptCallback)(DIO_tenPinName enPin);
} DIO__stPinSetup;


static const DIO__stPinSetup DIO__castPinSetup[DIO_nenNumberOfPins] =
{
/*                                            level   direction                      Interrupt   Resistor Enable   Peripheral Mode                  Drive Strength                   Callback            */
/* DIO_nenButton     */ {  MSP430_PIN_PA07,   tHi,    DIO_nen_DataDirection_Input,   True,       True,             DIO_nen_PortSelect_IO,           DIO_nen_DriveStrength_Reduced,   &DEB_vSetAppRequest  },
/* DIO_nenLED_Bar_1  */ {  MSP430_PIN_PA00,   tLo,    DIO_nen_DataDirection_Output,  False,      False,            DIO_nen_PortSelect_IO,           DIO_nen_DriveStrength_Reduced,   NULL                 },
/* DIO_nenLED_Bar_2  */ {  MSP430_PIN_PB06,   tLo,    DIO_nen_DataDirection_Output,  False,      False,            DIO_nen_PortSelect_IO,           DIO_nen_DriveStrength_Reduced,   NULL                 },
/* DIO_nenXTAL1      */ {  MSP430_PIN_PC00,   tLo,    DIO_nen_DataDirection_Input,   False,      False,            DIO_nen_PortSelect_Peripheral,   DIO_nen_DriveStrength_Reduced,   NULL                 },
/* DIO_nenXTAL2      */ {  MSP430_PIN_PC01,   tLo,    DIO_nen_DataDirection_Input,   False,      False,            DIO_nen_PortSelect_Peripheral,   DIO_nen_DriveStrength_Reduced,   NULL                 },
/* DIO_nenADC1       */ {  MSP430_PIN_PA08,   tLo,    DIO_nen_DataDirection_Input,   False,      False,            DIO_nen_PortSelect_Peripheral,   DIO_nen_DriveStrength_Reduced,   NULL                 },
/* DIO_nenADC2       */ {  MSP430_PIN_PA09,   tLo,    DIO_nen_DataDirection_Input,   False,      False,            DIO_nen_PortSelect_Peripheral,   DIO_nen_DriveStrength_Reduced,   NULL                 },
/* DIO_nenPWM        */ {  MSP430_PIN_PA10,   tLo,    DIO_nen_DataDirection_Output,  False,      False,            DIO_nen_PortSelect_Peripheral,   DIO_nen_DriveStrength_Reduced,   NULL                 },
/* DIO_nenTEST       */ {  MSP430_PIN_PA06,   tLo,    DIO_nen_DataDirection_Output,  False,      False,            DIO_nen_PortSelect_IO,           DIO_nen_DriveStrength_Reduced,   NULL                 },
/* DIO_nenTEST2      */ {  MSP430_PIN_PA04,   tLo,    DIO_nen_DataDirection_Output,  False,      False,            DIO_nen_PortSelect_IO,           DIO_nen_DriveStrength_Reduced,   NULL                 }
};

static void DIO__vISRCommon( uint8 u8ISRPort );

void DIO_vReset( void )
{
   DIO_tenPinName enPin;

   /* Obtain write access */
   PMAPKEYID = PMAPKEY;
   /* Map TA1CCR1 output to P2.2 */
   P2MAP2 = PM_TA1CCR1A;
   /* Lock */
   PMAPKEYID = 0;

   /* For lowest power consumption, unused IO should be outputs */
   PADIR = 0xFFFF;
   PAOUT = 0x0000;

   PBDIR = 0xFFFF;
   PBOUT = 0x0000;

   PCDIR = 0xFFFF;
   PCOUT = 0x0000;

   PJDIR = 0xFFFF;
   PJOUT = 0x0000;

   /* loop through all pins */
   for( enPin = (DIO_tenPinName)( DIO_nenNumberOfPins - 1 ); enPin >= DIO_nenFirstPin; enPin-- )
   {
      /* set direction */
      DIO_vSetDirection( enPin, DIO__castPinSetup[enPin].enDirection );

      /* set logic level */
      DIO_vSetOutput( enPin, DIO__castPinSetup[enPin].enLogicLevel );

      /* set interrupt enable bit */
      DIO_vSetInterruptEnable( enPin, DIO__castPinSetup[enPin].boInterruptEnable );

      /* set pull up/down resistor enable */
      DIO_vSetResistor( enPin, DIO__castPinSetup[enPin].boResistorEnable );

      /* set pin perihperal mode */
      DIO_vSetPortSelect( enPin, DIO__castPinSetup[enPin].enPeripheralMode );

      /* set pin drive strength */
      DIO_vSetDriveStrength( enPin, DIO__castPinSetup[enPin].enDriveStrength );
   }

   /* avoid any false interrupt triggers after powerup */
   PAIFG = 0x0000;

}

void DIO_vSetDirection( DIO_tenPinName enPin, DIO_tenDataDirection enDirection )
{
   uint32 u32Pin = DIO__castPinSetup[enPin].u32Pin;
   volatile MSP430_tstDIO *stGPIOPort = &MSP430_GPIO.stPort[u32Pin >> 4];

   if( enDirection == DIO_nen_DataDirection_Input )
   {
      stGPIOPort->u16Direction &= ~( 1 << ( u32Pin & 0x0F ) );
   }
   else
   {
      stGPIOPort->u16Direction |= ( 1 << ( u32Pin & 0x0F ) );
   }
}

void DIO_vSetOutput( DIO_tenPinName enPin, DIO_tenLogicLevel enLevel )
{
   uint32 u32Pin = DIO__castPinSetup[enPin].u32Pin;
   volatile MSP430_tstDIO *stGPIOPort = &MSP430_GPIO.stPort[u32Pin >> 4];

   if( enLevel == tHi )
   {
      stGPIOPort->u16Output |= ( 1 << ( u32Pin & 0x0F ) );
   }
   else
   {
      stGPIOPort->u16Output &= ~( 1 << ( u32Pin & 0x0F ) );
   }
}

void DIO_vToggleOutput( DIO_tenPinName enPin )
{
   uint32 u32Pin = DIO__castPinSetup[enPin].u32Pin;
   volatile MSP430_tstDIO *stGPIOPort = &MSP430_GPIO.stPort[u32Pin >> 4];

   stGPIOPort->u16Output ^= ( 1 << ( u32Pin & 0x0F ) );
}

DIO_tenLogicLevel DIO_enGetInput( DIO_tenPinName enPin )
{
   DIO_tenLogicLevel enRetVal = tLo;
   uint32 u32Pin = DIO__castPinSetup[enPin].u32Pin;
   volatile MSP430_tstDIO *stGPIOPort = &MSP430_GPIO.stPort[u32Pin >> 4];

   enRetVal = (DIO_tenLogicLevel)(( stGPIOPort->u16Input & ( 1 << ( u32Pin & 0x0F ) ) ) == ( 1 << ( u32Pin & 0x0F ) ) );

   return enRetVal;
}

void DIO_vSetResistor( DIO_tenPinName enPin, bool boEnable )
{
   uint32 u32Pin = DIO__castPinSetup[enPin].u32Pin;
   volatile MSP430_tstDIO *stGPIOPort = &MSP430_GPIO.stPort[u32Pin >> 4];

   if( boEnable == True )
   {
      stGPIOPort->u16ResistorEnable |= ( 1 << ( u32Pin & 0x0F ) );
   }
   else
   {
      stGPIOPort->u16ResistorEnable &= ~( 1 << ( u32Pin & 0x0F ) );
   }
}

void DIO_vSetDriveStrength( DIO_tenPinName enPin, DIO_tenDriveStrength enDriveStrength )
{
   uint32 u32Pin = DIO__castPinSetup[enPin].u32Pin;
   volatile MSP430_tstDIO *stGPIOPort = &MSP430_GPIO.stPort[u32Pin >> 4];

   if( enDriveStrength == DIO_nen_DriveStrength_Full )
   {
      stGPIOPort->u16DriveStrength |= ( 1 << ( u32Pin & 0x0F ) );
   }
   else
   {
      stGPIOPort->u16DriveStrength &= ~( 1 << ( u32Pin & 0x0F ) );
   }
}

void DIO_vSetPortSelect( DIO_tenPinName enPin, DIO_tenPortSelect enPortSelect )
{
   uint32 u32Pin = DIO__castPinSetup[enPin].u32Pin;
   volatile MSP430_tstDIO *stGPIOPort = &MSP430_GPIO.stPort[u32Pin >> 4];

   if( enPortSelect == DIO_nen_PortSelect_Peripheral )
   {
      stGPIOPort->u16PortSelect |= ( 1 << ( u32Pin & 0x0F ) );
   }
   else
   {
      stGPIOPort->u16PortSelect &= ~( 1 << ( u32Pin & 0x0F ) );
   }
}

void DIO_vSetInterruptEnable( DIO_tenPinName enPin, bool boInterruptEnable )
{
   uint32 u32Pin = DIO__castPinSetup[enPin].u32Pin;
   volatile MSP430_tstDIO *stGPIOPort = &MSP430_GPIO.stPort[u32Pin >> 4];

   /* only the 1st 2 ports have interrupts */
   if( ( u32Pin >> 4 ) < 2 )
   {
      if( boInterruptEnable == True )
      {
         stGPIOPort->u16InterruptEnable |= ( 1 << ( u32Pin & 0x0F ) );
      }
      else
      {
         stGPIOPort->u16InterruptEnable &= ~( 1 << ( u32Pin & 0x0F ) );
      }
   }
}

void DIO_vSetInterruptEdge( DIO_tenPinName enPin, DIO_tenInterruptEdgeSelect enEdgeSelect )
{
   uint32 u32Pin = DIO__castPinSetup[enPin].u32Pin;
   volatile MSP430_tstDIO *stGPIOPort = &MSP430_GPIO.stPort[u32Pin >> 4];

   /* only the 1st 2 ports have interrupts */
   if( ( u32Pin >> 4 ) < 2 )
   {
      if( enEdgeSelect == DIO_nen_InterruptEdge_RisingEdge )
      {
         stGPIOPort->u16InterruptEdgeSelect |= ( 1 << ( u32Pin & 0x0F ) );
      }
      else
      {
         stGPIOPort->u16InterruptEdgeSelect &= ~( 1 << ( u32Pin & 0x0F ) );
      }
   }
}

static void DIO__vISRCommon( uint8 u8ISRPort )
{
   DIO_tenPinName enPin;
   volatile MSP430_tstDIO *stGPIOPort = &MSP430_GPIO.stPort[u8ISRPort];
   uint32 u32Pin;
   uint8  u8Port;
   uint16 u16Mask;

   for( enPin = (DIO_tenPinName)( DIO_nenNumberOfPins - 1 ); enPin >= DIO_nenFirstPin; enPin-- )
   {
      /* which pin triggered this? */
      u32Pin = DIO__castPinSetup[enPin].u32Pin;
      u8Port = ( u32Pin >> 4 );
      u16Mask = ( 1 << ( u32Pin & 0x0F ) );

      if( u8Port == u8ISRPort )
      {
         /* Does this pin have its interrupt enabled? */
         if( ( stGPIOPort->u16InterruptEnable & u16Mask ) == u16Mask )
         {
            /* Does this pin have an interrupt pending? */
            if( ( stGPIOPort->u16InterruptFlag & u16Mask ) == u16Mask )
            {
               /* flip interrupt edge select */
               stGPIOPort->u16InterruptEdgeSelect ^= u16Mask;

               /* has a callback function been configured? */
               if ( DIO__castPinSetup[enPin].pfInterruptCallback != NULL )
               {
                  /* run it */
                  DIO__castPinSetup[enPin].pfInterruptCallback( enPin );
               }
            }
         }
      }
   }

   /* clear interrupt */
   stGPIOPort->u16InterruptFlag = 0;
}

// PORT 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void DIO_vPort1ISR( void )
{
   DIO__vISRCommon(0);
   /* wake up */
   _BIC_SR_IRQ(LPM3_bits);
}

// PORT 2 interrupt service routine
#pragma vector=PORT2_VECTOR
__interrupt void DIO_vPort2ISR( void )
{
   DIO__vISRCommon(1);
   /* wake up */
   _BIC_SR_IRQ(LPM3_bits);
}
